<!doctype html>
<html lang="zh-Hans">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>星辰聊天室demo</title>
    <link rel="stylesheet" href="https://cdn.staticfile.org/amazeui/2.7.2/css/amazeui.min.css">
    <link rel="stylesheet" href="https://cdn.staticfile.org/layer/2.3/skin/layer.css">
    <link rel="stylesheet" href="/css/main.css?v=120203">
    <script src="https://cdn.staticfile.org/vue/2.5.17-beta.0/vue.js"></script>
    <script src="https://cdn.staticfile.org/jquery/3.3.1/jquery.min.js"></script>
    <script src="https://cdn.staticfile.org/layer/2.3/layer.js"></script>
    <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
</head>

<body>
<div id="chat">
    <template>
        <div class="online_window">
            <div class="me_info">
                <div class="me_item">
                    <div class="me_avatar">
                        <img :src="currentUser.avatar" alt="">
                    </div>
                    <div class="me_status">
                        <div class="me_username">
                            <i class="am-icon am-icon-pencil" @click="changeName"></i> {{currentUser.username}}
                        </div>
                        <div class="me_income">{{currentUser.intro}}</div>
                    </div>
                    <div class="times-icon"><i class="am-icon am-icon-times"></i></div>
                </div>
            </div>
            <div class="online_list">
                <div class="online_list_header">群聊列表</div>
                <div class="online_item" v-for="user in roomUser" @contextmenu.prevent="select_user(user)">
                    <template v-if="user">
                        <div class="online_avatar">
                            <img :src="user.avatar" alt="">
                        </div>
                        <div class="online_status">
                            <div class="online_username">{{user.username}}</div>
                        </div>
                    </template>
                </div>
            </div>
            <div class="online_count">
                <h6>群聊人数 <span>{{currentCount}}</span> 位</h6>
            </div>
        </div>
        <div class="talk_window">
            <div class="windows_top">
                <div class="windows_top_left"><i class="am-icon am-icon-list online-list"></i> 欢迎来到星辰聊天室</div>
                <div class="windows_top_right">
                    <a href="http://yqy.ndstop.com" target="_blank"
                       style="color: #999">星辰网络博客</a>
                </div>
            </div>
            <div class="windows_body" id="chat-window" v-scroll-bottom>
                <ul class="am-comments-list am-comments-list-flip">
                    <template v-for="chat in roomChat">
                        <template v-if="chat.type === 'tips'">
                            <div class="chat-tips">
                                <span class="am-badge am-badge-primary am-radius">{{chat.content}}</span></div>
                        </template>
                        <template v-else>
                            <div v-if="chat.sendTime" class="chat-tips">
                                <span class="am-radius" style="color: #666666">{{chat.sendTime}}</span>
                            </div>
                            <article class="am-comment" :class="{ 'am-comment-flip' : chat.fd == currentUser.userFd }">
                                <a href="#link-to-user-home">
                                    <img :src="chat.avatar" alt="" class="am-comment-avatar"
                                         width="48" height="48"/>
                                </a>
                                <div class="am-comment-main">
                                    <header class="am-comment-hd">
                                        <div class="am-comment-meta">
                                            <a href="#link-to-user" class="am-comment-author">{{chat.username}}</a>
                                        </div>
                                    </header>
                                    <div class="am-comment-bd">
                                        <div class="bd-content">
                                            <template v-if="chat.type === 'text'" v-html="html_msg">
                                                {{chat.content}}
                                            </template>
                                            <template v-else-if="chat.type === 'image'">
                                                <img :src="chat.content" width="100%">
                                            </template>
                                            <template v-else v-html="html_msg">
                                                {{chat.content}}
                                            </template>
                                        </div>
                                    </div>
                                </div>
                            </article>
                        </template>
                    </template>
                </ul>
            </div>
            <div id="emoji_box" style="display: none;">

            </div>
            <div class="windows_input">
                <div class="am-btn-toolbar">
                    <div class="am-btn-group am-btn-group-xs">
                        <button type="button" class="am-btn" @click="picture"><i class="am-icon am-icon-picture-o"></i>
                        </button>
                        <input type="file" id="fileInput" style="display: none" accept="image/*">
                    </div>
                    <div class="am-btn-group am-btn-group-xs" style="width:38px;text-align: center;line-height: 28px;background: #dddddd;">
                        <img src="images/emoji.png" alt="表情" @click="emoji" style="width: 16px;height: 16px;cursor: pointer;">
                    </div>
                </div>
                <div class="input-box">
                    <label for="text-input" style="display: none"></label>
                    <textarea name="" id="text-input" cols="30" rows="10" title=""></textarea>
                </div>
                <div class="toolbar">
<!--                    <div class="left"><a href="http://www.easyswoole.com/" target="_blank">星辰网络博客</a>-->
<!--                    </div>-->
                    <div class="right">
                        <button class="send" @click="clickBtnSend">发送消息 ( Enter )</button>
                    </div>
                </div>
            </div>
        </div>
    </template>
</div>
<script>
    var private_fd=0;
    var current_fd=0;
    var Vm = new Vue({
        el        : '#chat',
        data      : {
            websocketServer  : "<?= $server ?>",
            websocketInstance: undefined,
            Reconnect        : false,
            ReconnectTimer   : null,
            HeartBeatTimer   : null,
            ReconnectBox     : null,
            currentUser      : {username: '-----', intro: '-----------', fd: 0, avatar: 0},
            roomUser         : {},
            roomChat         : [],
            up_recv_time     : 0,
        },
        created   : function () {
            this.connect();
        },
        mounted   : function () {
            var othis = this;
            var textInput = $('#text-input');
            textInput.on('keydown', function (ev) {
                if (ev.keyCode == 13 && ev.shiftKey) {
                    textInput.val(textInput.val() + "\n");
                    return false;
                } else if (ev.keyCode == 13) {
                    othis.clickBtnSend();
                    ev.preventDefault();
                    return false;
                }
            });
            $('.online-list').on('click', function () {
                $('.online_window').show();
                $('.windows_input').hide();
            });
            $('.times-icon').on('click', function () {
                $('.online_window').hide();
                $('.windows_input').show();
            });
            var input = document.getElementById("fileInput");
            input.addEventListener('change', readFile, false);

            function readFile() {
                var file = this.files[0];
                //判断是否是图片类型
                if (!/image\/\w+/.test(file.type)) {
                    alert("只能选择图片");
                    return false;
                }
                if (file.size > 1048576) {
                    alert('图片大小不能超过1MB');
                    return false;
                }
                var reader = new FileReader();
                reader.readAsDataURL(file);
                reader.onload = function (e) {
                    othis.broadcastImageMessage(this.result)
                }
            }
        },
        methods   : {
            connect              : function () {
                var othis = this;
                var username = localStorage.getItem('username');
                var websocketServer = this.websocketServer;
                if (username) {
                    console.log(username)
                    websocketServer += '?username=' + encodeURIComponent(username)
                }
                this.websocketInstance = new WebSocket(websocketServer);
                this.websocketInstance.onopen = function (ev) {
                    // 断线重连处理
                    if (othis.ReconnectBox) {
                        layer.close(othis.ReconnectBox);
                        othis.ReconnectBox = null;
                        clearInterval(othis.ReconnectTimer);
                    }
                    // 前端循环心跳 (1min)
                    othis.HeartBeatTimer = setInterval(function () {
                        othis.websocketInstance.send('PING');
                    }, 1000 * 30);
                    // 请求获取自己的用户信息和在线列表
                    othis.release('index', 'info');
                    othis.release('index', 'online');
                    othis.websocketInstance.onmessage = function (ev) {
                        try {
                            var data = JSON.parse(ev.data);
                            // console.log(data)
                            if (data.sendTime) {
                                if (othis.up_recv_time + 10 * 1000 > (new Date(data.sendTime)).getTime()) {
                                    othis.up_recv_time = (new Date(data.sendTime)).getTime();
                                    data.sendTime = null;
                                } else {
                                    othis.up_recv_time = (new Date(data.sendTime)).getTime();
                                }
                            }
                            switch (data.action) {
                                case 101: {
                                    // 收到管理员消息
                                    othis.roomChat.push({
                                        type    : data.type ? data.type : 'text',
                                        fd      : 0,
                                        content : data.content,
                                        avatar  : 'images/head_1.jpg',
                                        username: '群聊管理员'
                                    });
                                    break;
                                }
                                case 103 : {
                                    // 收到用户消息
                                    var broadcastMsg = {
                                        type    : data.type,
                                        fd      : data.fromUserFd,
                                        content : analysis_emoji(data.content),
                                        avatar  : othis.roomUser['user' + data.fromUserFd].avatar,
                                        username: othis.roomUser['user' + data.fromUserFd].username,
                                        sendTime: data.sendTime
                                    };
                                    othis.roomChat.push(broadcastMsg);
                                    break;
                                }
                                case 104 : {
                                    // 收到最后消息
                                    var lastMsg = {
                                        type    : data.type,
                                        fd      : data.fromUserFd,
                                        content : analysis_emoji(data.content),
                                        avatar  : data.avatar,
                                        username: data.username,
                                        sendTime: data.sendTime
                                    };
                                    othis.roomChat.push(lastMsg);
                                    break;
                                }
                                case 105 : {
                                    // 收到私聊消息
                                    private_fd = data.fd;
                                    current_fd = othis.currentUser.fd;
                                    if(data.type === 'text'){
                                        layer.open({
                                            type: 1,
                                            skin: 'layui-layer-rim', //加上边框
                                            area: ['420px', '240px'], //宽高
                                            content: "<div class='content'>收到来自"+data.username+"的消息："+data.content+"</div><br><input id='s_content' type='text' ><br><button onclick='sendmsg()'>回复</button>"
                                        });
                                    } else if (data.type === 'image'){
                                        layer.open({
                                            type: 1,
                                            skin: 'layui-layer-rim', //加上边框
                                            area: ['420px', '240px'], //宽高
                                            content: "<div class='content'>收到来自"+data.username+"的消息：<img src='"+data.content+"'></div><br><input id='s_content' type='text' ><br><button onclick='sendmsg()'>回复</button>"
                                        });
                                    }

                                    break;
                                }
                                case 201: {
                                    // 刷新自己的用户信息
                                    othis.currentUser.intro = data.intro;
                                    othis.currentUser.avatar = data.avatar;
                                    othis.currentUser.fd = data.userFd;
                                    othis.currentUser.username = data.username;
                                    break;
                                }
                                case 202: {
                                    // 刷新当前的在线列表
                                    othis.roomUser = data.list;
                                    break;
                                }
                                case 203: {
                                    // 新用户上线
                                    othis.$set(othis.roomUser, 'user' + data.info.fd, data.info);
                                    othis.roomChat.push({
                                        type   : 'tips',
                                        content: '用户 ' + data.info.username + ' 已进入群聊',
                                    });
                                    break;
                                }
                                case 204: {
                                    // 用户已离线
                                    var username = othis.roomUser['user' + data.userFd].username;
                                    othis.$delete(othis.roomUser, 'user' + data.userFd);
                                    othis.roomChat.push({
                                        type   : 'tips',
                                        content: '用户 ' + username + ' 已退出群聊',
                                    });
                                    break;
                                }
                                case 205: {
                                    // 用户@
                                    layer.msg(data.content);
                                    break;
                                }
                            }
                        } catch (e) {
                            console.warn(e);
                        }
                    };
                    othis.websocketInstance.onclose = function (ev) {
                        othis.doReconnect();
                    };
                    othis.websocketInstance.onerror = function (ev) {
                        othis.doReconnect();
                    }
                }
            },
            doReconnect          : function () {
                var othis = this;
                clearInterval(othis.HeartBeatTimer);
                othis.ReconnectBox = layer.msg('已断开，正在重连...', {
                    scrollbar : false,
                    shade     : 0.3,
                    shadeClose: false,
                    time      : 0,
                    offset    : 't'
                });
                othis.ReconnectTimer = setInterval(function () {
                    othis.connect();
                }, 1000)
            },
            /**
             * 向服务器发送消息
             * @param controller 请求控制器
             * @param action 请求操作方法
             * @param params 携带参数
             */
            release              : function (controller, action, params) {
                controller = controller || 'index';
                action = action || 'action';
                params = params || {};
                var message = {controller: controller, action: action, params: params}
                this.websocketInstance.send(JSON.stringify(message))
            },
            /**
             * 发送文本消息
             * @param content
             */
            broadcastTextMessage : function (content) {
                this.release('broadcast', 'roomBroadcast', {content: content, type: 'text'})
            },
            /**
             * 发送图片消息
             * @param base64_content
             */
            broadcastImageMessage: function (base64_content) {
                this.release('broadcast', 'roomBroadcast', {content: base64_content, type: 'image'})
            },
            picture              : function () {
                var input = document.getElementById("fileInput");
                input.click();
            },
            emoji              : function () {
                console.log(this.roomChat)
                // 显示表情
                var emoji_div = document.getElementById("emoji_box");

                if(emoji_div.style.display === 'block'){
                    emoji_div.style.display = 'none';
                } else {
                    emoji_div.style.display = 'block';
                    this.createEmoji();
                }
            },
            createEmoji : function(){
                // 创建表情
                var row = 8, col = 10;
                var str = '<table class="emoji">';
                for (var i = 0; i < row; i++) {
                    str += '<tr>';
                    for (var j = 0; j < col; j++) {
                        var n = i * col + j;
                        str += '<td>' + (n > 71 ? '' : ('<img onclick="select_emoji(' + n + ');" src="images/emoji/' + n + '.gif" />')) + '</td>';
                    }
                    str += '</tr>';
                }
                str += '</table>';

                $("#emoji_box").html(str);
            },
            select_user : function (user){
                layer.confirm('请选择操作？', {
                    btn: ['私信','@用户'] //按钮
                }, function(){
                    // 跳转到私聊页面并带上私聊对象的信息
                    var current_fd = Vm.currentUser.fd;
                    var current_username = Vm.currentUser.username;
                    window.open('/Chat?fd='+user.fd+'&username='+user.username+'&current_username='+current_username+'&current_fd='+current_fd);
                    layer.closeAll();
                }, function(){
                    // cursor_insert(user_input,'@'+user.username+' ');
                    // 直接给服务器发送消息，简易@
                    Vm.release('broadcast', 'roomBroadcast', {content: user.fd, type: '@'});
                    layer.closeAll();
                });
            },
            /**
             * 点击发送按钮
             * @return void
             */
            clickBtnSend         : function () {
                var textInput = $('#text-input');
                var content = textInput.val();
                if (content.trim() !== '') {
                    if (this.websocketInstance && this.websocketInstance.readyState === 1) {
                        this.broadcastTextMessage(content);
                        textInput.val('');
                    } else {
                        layer.tips('连接已断开', '.windows_input', {
                            tips: [1, '#ff4f4f'],
                            time: 2000
                        });
                    }
                } else {
                    layer.tips('请输入消息内容', '.windows_input', {
                        tips: [1, '#3595CC'],
                        time: 2000
                    });
                }
            },
            changeName           : function () {
                layer.prompt({title: '拒绝吃瓜，秀出你的昵称', formType: 0}, function (username, index) {
                    if (username) {
                        localStorage.setItem('username', username);
                        window.location.reload();
                    }
                    layer.close(index);
                });

            }
        },
        computed  : {
            currentCount() {
                return Object.getOwnPropertyNames(this.roomUser).length - 1;
            }
        },
        directives: {
            scrollBottom: {
                componentUpdated: function (el) {
                    el.scrollTop = el.scrollHeight
                }
            }
        }
    });

    // 输入框
    var user_input = document.getElementById("text-input");

    // 选择表情
    function select_emoji(n)
    {
        cursor_insert(user_input, '{#' + n + '}');
        $("#emoji_box").fadeOut();
    }

    // 私聊
    function sendmsg()
    {
        var content = $("#s_content").val();
        if(content == ''){
            layer.msg('请先输入发送的内容');
        }
        console.log(private_fd)
        console.log(current_fd)
        Vm.release('broadcast', 'roomBroadcast', {content: content, type: 'text',tofd:private_fd,current_fd:current_fd})
    }

    // 光标处插入内容
    function cursor_insert(obj, txt) {
        if (document.selection) {
            obj.selection.createRange().text = txt;
        } else {
            var v = obj.value;
            var i = obj.selectionStart;
            obj.value = v.substr(0, i) + txt + v.substr(i);
            user_input.focus();
            obj.selectionStart = i + txt.length;
        }
    }

    // 解析消息中的表情
    function analysis_emoji(str) {
        var p = /{#(\d|[1-6]\d|7[01])}/;
        if (p.test(str)) {
            return analysis_emoji(str.replace(p, "<img src='images/emoji/$1.gif'/>"))
        } else {
            return str;
        }
    }


</script>
</body>
</html>